__author__ = "JLocke"
__date__ = "$Sep 24, 2009 1:56:01 AM$"

from game_card import GameCard
from game_state import GameState
import math
from pyglet import font
import pyglet
import pyglet.clock
import pyglet.resource
import random

#settings info
#number of cards in the game (forced to be even)
numOfCards = None
#file format of the images
resFormatImg = None
#path to images for game cards
resPathImg = None
#size modifier for array division
sizeMod = None
#screen width and height
sWidth = sHeight = None
#number of seconds (float) that the game waits after a guess is wrong
#before turning the cards back face down
wrongDelay = None
#used as extra card in games with an odd number of pairs
blankCard = None

#used for positioning cards in the x and y direction
cardHeight = None
cardWidth = None
xAdjust = None
yAdjust = None

#menu info
menuCount = None
menuTimed = None
menuExit = None

endString = None
flipCountText = None
gameTimerText = None

#in-game info
cardBack = None
cardFronts = []
gameWindow = None
gameState = None
GameStates = None
gameBoard = []

#card pairing info
cardOne, cardTwo = None, None
firstCard = None
holding = None
numOfMatches = None

gameTimer = None
flipCount = None

def config_file():
    global cardHeight, cardWidth, numOfCards, resFormatImg, resPathImg, sizeMod, sHeight, sWidth, wrongDelay, xAdjust, yAdjust

    sWidth = 800
    sHeight = 600
    cardWidth = 110
    cardHeight = 82

    numOfCards = 16
    sizeMod = math.sqrt(numOfCards)
    xAdjust = (sWidth - (120 * sizeMod)) // 2
    yAdjust = 10

    resFormatImg = 'png'
    resPathImg = 'imgs'

    wrongDelay = 0.75

#returns True if cardOne and cardTwo match; False otherwise
def do_cards_match():
    return cardOne.cardCode == cardTwo.cardCode

#game is over, change the game state and set up string for end game display
def game_over():
    global endString, gameState
    
    if gameState == GameStates.COUNT_GAME:
        endString.text = 'Number of guesses: ' + str(flipCount)
    elif gameState == GameStates.TIMED_GAME:
        pyglet.clock.unschedule(update)
        endString.text = 'Time elapsed: ' + str(round(gameTimer, 1)) + ' secs'
    
    gameState = GameStates.END_GAME

#load resources to be used for the game
def load_resources():
    global blankCard, cardBack, cardFronts, menuCount, menuTimed, menuExit
    
    pyglet.resource.path = [resPathImg]
    pyglet.resource.reindex()

    cardFronts = [None for cf in range(numOfCards // 2)]

    name = 'back' + '.' + resFormatImg
    cardBack = pyglet.resource.image(name)

    for sub in range(numOfCards // 2):
        name = 'card' + str(sub) + '.' + resFormatImg
        cardFronts[sub] = pyglet.resource.image(name)

    blankCard = pyglet.resource.image('multi.png')

    menuCount = pyglet.resource.image('count-game.png')
    menuTimed = pyglet.resource.image('timed-game.png')
    menuExit = pyglet.resource.image('exit-game.png')

#initialize variables for gameplay
def init():
    global endString, firstCard, flipCountText, gameBoard, gameState, GameStates, gameTimerText, gameWindow

    #set game as being in menu state
    GameStates = GameState()
    gameState = GameState()
    gameState = GameStates.GAME_MENU

    #prepare window
    gameWindow = pyglet.window.Window(sWidth, sHeight)
    gameWindow.set_caption("Memorize Me v2 by JLocke")

    #set up score keeping
    arial = font.load('Arial', 14)
    flipCountText = pyglet.font.Text(arial, x=500, y=570)
    gameTimerText = pyglet.font.Text(arial, x=500, y=570)

    arialBig = font.load('Arial', 18)
    endString = pyglet.font.Text(arialBig, x=285, y=400)

    firstCard = True
    holding = False

    #prepare game board
    gameBoard = [[None for col in range(int(numOfCards // sizeMod))] for row in range(int(numOfCards // sizeMod))]
    #init card backs (same for all cards)
    for i in range(int(numOfCards // sizeMod)):
        for j in range(int(numOfCards // sizeMod)):
            gameBoard[i][j] = GameCard()
            gameBoard[i][j].cardBack = cardBack

    #init card fronts and matching codes
    second = False
    code = 0
    for i in range(int(numOfCards // sizeMod)):
        for j in range(int(numOfCards // sizeMod)):
            #if there are an odd number of cards, insert dummy card faceUp
            if(i == j and i == int(numOfCards // sizeMod)-1 and int(sizeMod % 2) == 1):
                gameBoard[i][j].cardFront = blankCard
                gameBoard[i][j].faceUp = True
                break

            gameBoard[i][j].cardFront = cardFronts[code]
            gameBoard[i][j].cardCode = code

            if second:
                second = False
                code += 1
            else:
                second = True

#returns None if x, y coordinate is not on a card
#returns a tuple of the gameBoard location (x, y) of the card that was clicked
#  if the passed coordinates are on a card
def is_on_card(x, y):
    for i in range(int(sizeMod)):
        for j in range(int(sizeMod)):
            if j * 120 + xAdjust <= x <= j * 120 + xAdjust + cardWidth and i * 92 + yAdjust <= y <= i * 92 + yAdjust + cardHeight:
                return (i, j)

    return None

#sets all cards to face down and shuffles the cards within the game board
def randomize_cards():
    #set all cards to face down
    for i in range(int(numOfCards // sizeMod)):
        for j in range(int(numOfCards // sizeMod)):
            gameBoard[i][j].faceUp = False

    #randomize card layout
    for i in range(int(numOfCards // sizeMod)):
        for j in range(int(numOfCards // sizeMod)):
            rand1 = random.randint(0, numOfCards // sizeMod-1)
            rand2 = random.randint(0, numOfCards // sizeMod-1)
            temp = gameBoard[i][j]
            gameBoard[i][j] = gameBoard[rand1][rand2]
            gameBoard[rand1][rand2] = temp

#register the event handlers
def register_events():
    gameWindow.on_draw = on_draw
    gameWindow.on_mouse_release = on_mouse_release

#sets up variables to play a count game
def setup_count_game():
    global flipCount, gameState, numOfMatches

    randomize_cards()
    numOfMatches = 0
    gameState = GameStates.COUNT_GAME
    flipCount = 0

#sets up variables to play a timed game
def setup_timed_game():
    global gameState, gameTimer, numOfMatches

    randomize_cards()
    numOfMatches = 0
    gameState = GameStates.TIMED_GAME
    gameTimer = 0.0
    pyglet.clock.schedule_interval(update, 0.5)

#used to keep the second chosen card showing for a time after it is selected
#before it is hidden again
def show_card(dt):
    global holding
    
    cardOne.faceUp = False
    cardTwo.faceUp = False

    holding = False

#displays the appropriate "score" to the screen -- time or number of guesses
def show_measurement():
    global flipCountText, gameTimerText

    if(gameState == GameStates.COUNT_GAME):
        flipCountText.text = 'Number of guesses: ' + str(flipCount)
        flipCountText.draw()
    else:   #gameState == GamesStates.TIMED_GAME
        gameTimerText.text = 'Time elapsed: ' + str(round(gameTimer, 1)) + ' secs'
        gameTimerText.draw()

def update(dt):
    global gameTimer

    if gameState == GameStates.TIMED_GAME:
        #gameTimer += dt
        gameTimer += 0.5

def on_mouse_release(x, y, button, modifiers):
    global cardOne, cardTwo, firstCard, flipCount, gameState, gameTimer, holding, numOfMatches

    if(gameState == GameStates.COUNT_GAME or gameState == GameStates.TIMED_GAME):
        if button == pyglet.window.mouse.LEFT and not holding:
            #if the click is on a card
            onCard = is_on_card(x, y)
            if onCard:
                #if clicked card is not faceUp
                if not gameBoard[onCard[0]][onCard[1]].faceUp:
                    #if this is the first card of the guess pairing
                    if firstCard:
                        cardOne = gameBoard[onCard[0]][onCard[1]]
                        gameBoard[onCard[0]][onCard[1]].faceUp = True
                        firstCard = False
                    else:
                        cardTwo = gameBoard[onCard[0]][onCard[1]]
                        gameBoard[onCard[0]][onCard[1]].faceUp = True

                        firstCard = True
                        if do_cards_match():
                            numOfMatches += 1
                        else:
                            pyglet.clock.schedule_once(show_card, wrongDelay)
                            holding = True

                        #if this is a count game, increment count
                        if gameState == GameStates.COUNT_GAME:
                            flipCount += 1

                        if numOfMatches == len(cardFronts):
                            game_over()

    elif(gameState == GameStates.END_GAME):
        if button == pyglet.window.mouse.LEFT:
            #if the exit button is clicked, exit game
            if(x > 299 and x < 501 and y > 50 and y < 132):
                pyglet.app.exit()

            #if the count game button is clicked, set up game for count game
            if(x > 87 and x < 289 and y > 100 and y < 182):
                setup_count_game()

            #if the timed game button is clicked, set up game for timed game
            if(x > 511 and x < 713 and y > 100 and y < 182):
                setup_timed_game()
    
    else:   #GameStates.GAME_MENU
        if button == pyglet.window.mouse.LEFT:
            #if the exit button is clicked, exit game
            if(x > 299 and x < 501 and y > 200 and y < 282):
                pyglet.app.exit()

            #if the count game button is clicked, set up game for count game
            if(x > 150 and x < 352 and y > 400 and y < 482):
                setup_count_game()

            #if the timed game button is clicked, set up game for timed game
            if(x > 448 and x < 650 and y > 400 and y < 482):
                setup_timed_game()


def on_draw():
    gameWindow.clear()

    if(gameState == GameStates.COUNT_GAME or gameState == GameStates.TIMED_GAME):
        #draw all cards
        for i in range(int(sizeMod)):
            for j in range(int(sizeMod)):
                gameBoard[i][j].draw(120 * j + xAdjust, 92 * i + yAdjust)

        #draw gametype-specific measurement (timer or flip count)
        show_measurement()

    elif(gameState == GameStates.END_GAME):
        endString.draw()
        menuCount.blit(87, 100)
        menuExit.blit(299, 50)
        menuTimed.blit(511, 100)

    else:   #GameStates.GAME_MENU
        menuCount.blit(150, 400)
        menuTimed.blit(448, 400)
        menuExit.blit(299, 200)

if __name__ == "__main__":
    config_file()
    load_resources()
    init()
    register_events()
    
    pyglet.app.run()
